function GfxFont(imgSrc, imgHeight, imgWidth, chrHeight, chrWidth, chrSet){
    
    this.img = imgSrc;
    this.imgHeight = imgHeight;
    this.imgWidth = imgWidth;
    this.chrHeight = chrHeight;
    this.chrWidth = chrWidth;
    this.chrSet = chrSet;
    this.isLoaded = false;
    
    var chrMap = [];
    var fontBuffer = null;
    var that = this;
        
    var createOffscreenCanvas = function(w,h){
        var buffer = document.createElement('canvas');
        buffer.width = w;
        buffer.height = h;
        return buffer;        
    };
        
    var loadFontImage=function(cbFinishedLoading){    
        var context = fontBuffer.getContext('2d');
        var image = new Image();
        image.onload = function(e){
            context.drawImage(e.target,0,0);
            createCharacterMap();
            cbFinishedLoading();
        };
        image.src = that.img;
    };

    var createCharacterMap = function(){
        // need to fit to the character matrix (e.g. buffered image)
        var chrWidth = that.chrWidth;
        var chrHeight = that.chrHeight;
        var imgWidth = that.imgWidth;
        var chrSet = that.chrSet;
    
        for(var i=0; i<chrSet.length; ++i){
            chrMap[chrSet[i]] = { 
                'x' : ( chrWidth * i ) % imgWidth, 
                'y' : ((( chrWidth * i ) / imgWidth)<<0) * chrHeight
            };                         
        }
    };

    // callback signature: function(imageBuffer : canvas)  
    this.load = function(onLoadFinished){        
        fontBuffer = createOffscreenCanvas(imgWidth, imgHeight);
        loadFontImage( function(){
            that.isLoaded = true;
            onLoadFinished();
        });
    };

    this.drawGfxText = function(canvas, text, x, y){
        
        if(!that.isLoaded){
            console.error("GfxFont not loaded - use GfxFont.load() first!");
            return;
        }        
        
        var bufferContext = fontBuffer.getContext('2d');
        var context = canvas.getContext('2d');
        for(var i=0; i<text.length; ++i){
            var c = chrMap[text[i]];
            var ic = bufferContext.getImageData( c.x, c.y, that.chrWidth, that.chrHeight );
            context.putImageData(ic, (i*that.chrWidth)+x, y);
        }
    };
    

    this.createGfxTextImage = function(text){
        
        var image = document.createElement('canvas');
        image.width = that.chrWidth * text.length;
        image.height = that.chrHeight;
        
        that.drawGfxText(image, text, 0, 0);
        
        return image;
    };    
}


function Scrolltext(cfg){

    var textImage = null;
    var context = null;
    var pos = [];
    var config = cfg;
    
    var initialize = function(){
                
        var canvas = document.getElementById('fg');
        context = canvas.getContext('2d');
        var gfxFont = new GfxFont(config.fontImage, config.imageWidth, config.imageHeight, config.fontWidth, config.fontHeight, config.fontSet );
        gfxFont.load(function(){

            pos[0] = canvas.width; // left to right scrolling
            pos[1] = config.posY;
            
            textImage = gfxFont.createGfxTextImage(config.text);
                                    
            requestAnimationFrame(update);
        });
        
    };
        
    var update = function(){
        context.clearRect(pos[0], pos[1], textImage.width, textImage.height);            
        
        pos[0] -= config.speed;        
        if(pos[0]< -textImage.width+20){
            pos[0] = context.canvas.width;
        }        
        
        context.save();    
        context.translate( pos[0], pos[1] );
        context.drawImage(textImage, 0, 0);
        context.restore();
                
        requestAnimationFrame(update);
    };
    
    
    initialize();
    
}